#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/msg.h>
#include "Frame.h"

unsigned int Message::n_register=0;

extern "C" Message* create_t()
{
  return new Message;
}

extern "C" void destroy_t( Message* object )
{
  delete object;
}


inline unsigned int Message::Get_n_register(){
   
    return(n_register+=rand()% 10 + 1);
}

inline Message::Message(){
    bzero((char *) &hdr, sizeof(hdr));
    bzero((char *) &pkg, sizeof(pkg));
    
    
}

inline Message::~Message(){}
    
inline void Message::Fill_header(unsigned char &domain, unsigned int &id_ss, unsigned int &id_sd){
     bzero((char *) &hdr, sizeof(hdr));
     hdr.version=version;
     hdr.type_message=NULL_MESSAGE_TYPE;
     hdr.offset=NO_OFFSET;
     hdr.domain=domain;
     hdr.flag=NO_FLAG;
     hdr.id_server_source=id_ss;
     hdr.id_server_destination=id_sd;
    
}

inline void Message::Membership(){  
     bzero((char *) &pkg, sizeof(pkg));
     hdr.type_message=MEMBERSHIP;
     pkg.header=hdr;
}

inline void Message::Investment(unsigned short &flush_t){
   
     bzero((char *) &pkg, sizeof(pkg));
     hdr.type_message=INVESTMENT;
     pkg.header=hdr;
     pkg.f_time=flush_t;
}

inline void Message::Guard_report(sockaddr_in &id_c){
    bzero((char *) &pkg, sizeof(pkg));
    hdr.type_message=GUARD_REPORT;
    pkg.header=hdr;
    pkg.id_client=id_c;
}

inline void Message::Missing_report(struct sockaddr_in &id_c, unsigned int &ack, 
                                    unsigned int &seq, unsigned int& n_seq){
    
    bzero((char *) &pkg, sizeof(pkg));
    hdr.type_message=MISSING_REPORT;
    pkg.header=hdr;
    pkg.id_client=id_c;
    pkg.acknowledge_number=ack;
    pkg.sequence_number=seq;
    pkg.next_sequence_number=n_seq;
}

inline void Message::Adoption_papers(struct sockaddr_in &id_c, unsigned int &ack, 
                                     unsigned int &seq, unsigned int& n_seq, 
                                     struct sockaddr_in &ip_sg, unsigned int &id_sg){
    bzero((char *) &pkg, sizeof(pkg));
    hdr.type_message=ADOPTION_PAPERS;
    pkg.header=hdr;
    pkg.id_client=id_c;
    pkg.acknowledge_number=ack;
    pkg.sequence_number=seq;
    pkg.next_sequence_number=n_seq;
    pkg.ip_server_guardian=ip_sg;
    pkg.id_server_guardian=id_sg;
}

inline void Message::Bookmark(struct sockaddr_in &id_c, unsigned int &ack, 
                              unsigned int &seq, unsigned int& n_seq, 
                              struct sockaddr_in &ip_sg, unsigned int &id_sg, Buffer &buff){
    
    bzero((char *) &pkg, sizeof(pkg));
    hdr.type_message=BOOKMARK;
    hdr.offset=buff.size;
    pkg.header=hdr;
    pkg.id_client=id_c;
    pkg.acknowledge_number=ack;
    pkg.sequence_number=seq;
    pkg.next_sequence_number=n_seq;
    pkg.ip_server_guardian=ip_sg;
    pkg.id_server_guardian=id_sg;
    
    packet = (Book*)malloc(sizeof(Package)+buff.size);
    packet->p=pkg;
    memcpy(packet->payload, buff.buff, buff.size);
    
}

inline void Message::Confirmation(Package &gkp){
   
   bzero((char *) &pkg, sizeof(pkg));
   pkg=gkp;
   pkg.header.id_server_source=gkp.header.id_server_destination;
   pkg.header.id_server_destination=gkp.header.id_server_destination;
   
   switch(gkp.header.type_message){
       
                    case INVESTMENT:{ 
                         pkg.header.type_message=INVESTMENT_CONFIRMATION;
                         break;
                    }
                    case GUARD_REPORT:{
                         pkg.header.type_message=GUARD_CONFIRMATION;
                         break;
                    }
     
                     case MISSING_REPORT:{
                         pkg.header.type_message=MISSING_CONFIRMATION;
                         break;
                    }
                  
   }
}

int Message_queue_setup(const int QUEUE_KEY){
    
    key_t key;
    int queue_id;
       
    key = ftok ("/bin/ls", QUEUE_KEY);
    if (key == (key_t)-1)
    {
	return(EXIT_FAILURE);
    } 
        queue_id = msgget (key, 0600 | IPC_CREAT);
        
    if (queue_id==-1)
    {
	return(EXIT_FAILURE);
    }
    else
        return(queue_id);
}
























/*
class membership;
class Investment;
class Guard;
class Missing_report;
class Adoption_papers;

class Header{

public: 
    Header()
    {
        this->message.header.version=                 version;
        this->message.header.type_message=            NULL_MESSAGE_TYPE;
        this->message.header.flag=                    NO_FLAG;
        this->message.header.offset=                  NO_VALID_OFFSET;
    }
    Header(char&, unsigned char&, unsigned int&, unsigned int&); 
    Header(const Header& orig);
    virtual ~Header(){};
    
    void Get_time_message(date_time_prototype&);
    int  System_interface(char*,std::string, int);
    
    static const unsigned char      NULL_MESSAGE_TYPE=0xAA;
    static const unsigned short     NO_VALID_OFFSET=0xFFFF;
    static const char               NO_FLAG=0;                
    
    package                         message;
    message_size                    size;    
};

Header::Header(char& version, unsigned char& domain, unsigned int& id_server_source, 
               unsigned int& id_server_destination)
{
    this->message.header.version=                 version;
    this->message.header.type_message=            NULL_MESSAGE_TYPE;
    this->message.header.domain=                  domain;
    this->message.header.id_server_source=        id_server_source;
    this->message.header.id_server_destination=   id_server_destination;
    this->message.header.flag=                    NO_FLAG;
    this->message.header.offset=                  NO_VALID_OFFSET;
    
}

Header::Header(const Header& orig)
{
    message.header=orig.message.header;
}

inline int Header::System_interface(char *ref,std::string c, int n_bytes)
{
    FILE *in;
    char *command=new char[c.size()+1];
    command[c.size()]=0;
    memcpy(command,c.c_str(),c.size());
    in = popen(command ,"r");
    fgets(ref,n_bytes,in);
    pclose(in);
    delete[] command, in;
    return (EXIT_SUCCESS);
}

inline void Header::Get_time_message(date_time_prototype &time)
{
    System_interface(time.nanoseconds,"date +%N", sizeof(time.nanoseconds));
    System_interface(time.seconds,"date +%S", sizeof(time.seconds));
    System_interface(time.minutes,"date +%M", sizeof(time.minutes));
    System_interface(time.hour,"date +%k", sizeof(time.hour));
    System_interface(time.day,"date +%d", sizeof(time.day));
    System_interface(time.month,"date +%m", sizeof(time.month));
    System_interface(time.year,"date +%G",sizeof(time.year));   
}





class Membership: public Header{

public:
    Membership(Header &h)
    {   
        bzero((char *) &this->message, sizeof(this->message));
        this->message.header=h.message.header;
        this->message.header.type_message=MEMBERSHIP;
        this->message.header.offset=MEMBERSHIP_OFFSET;
        this->message.header.flag=NO_FLAG;
 
    }
private:
    static const unsigned short MEMBERSHIP_OFFSET=0;
};




class Investment : public Header  {    

public:
    Investment(Header &h, date_time_prototype &flush_time)
    {
        
        bzero((char *) &this->message, sizeof(this->message));
        this->message.date_time=flush_time; 
        this->message.header=h.message.header;
        this->message.header.type_message=INVESTMENT;
        this->message.header.offset=INVESTMENT_OFFSET;
        this->message.header.flag=NO_FLAG;      
    }
    virtual ~Investment(){}

private:
    static const unsigned short INVESTMENT_OFFSET=sizeof(date_time_type);
    
};





class Guard : public Header {

public:
    Guard(Header &h, unsigned int &s_n, unsigned int &a_n, struct sockaddr_in &id_c)
    {
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=id_c;
        this->message.sequence_number=s_n;
        this->message.acknowledge_number=a_n;
        this->message.header=h.message.header;
        this->message.header.type_message=GUARD_REPORT;
        this->message.header.offset=GUARD_OFFSET;
        this->message.header.flag=NO_FLAG; 
 
    }
    
    Guard(package& g)
    {    
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=g.id_client;
        this->message.sequence_number=g.sequence_number;
        this->message.acknowledge_number=g.acknowledge_number;
        this->message.header=g.header;
        this->message.header.type_message=GUARD_CONFIRMATION;
        this->message.header.offset=GUARD_OFFSET;
        this->message.header.flag=NO_FLAG; 
    }

    virtual ~Guard(){}    

private:
    static const unsigned short GUARD_OFFSET=sizeof(size.guard);
   
};





class Missing_report : public Header  {
    
public:
    Missing_report(Header &h, unsigned int &s_n, unsigned int &a_n, 
                   unsigned int &n_sn, struct sockaddr_in &id_c)
    {
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=id_c;
        this->message.sequence_number=s_n;
        this->message.acknowledge_number=a_n;
        this->message.next_sequence_number=n_sn;
        this->message.header=h.message.header;
        this->message.header.type_message=MISSING_REPORT;
        this->message.header.offset=MISSING_REPORT_OFFSET;
        this->message.header.flag=NO_FLAG;
    }
   
    Missing_report(package& m_r)
    {     
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=m_r.id_client;
        this->message.sequence_number=m_r.sequence_number;
        this->message.acknowledge_number=m_r.acknowledge_number;
        this->message.next_sequence_number=m_r.next_sequence_number;
        this->message.header=m_r.header;
        this->message.header.type_message=MISSING_CONFIRMATION;
        this->message.header.offset=MISSING_REPORT_OFFSET;
        this->message.header.flag=NO_FLAG;
    }
    
    virtual ~Missing_report(){}
 
private:
    static const unsigned short MISSING_REPORT_OFFSET=sizeof(size.missing);

};





class Adoption_papers : public Header {

public:
    Adoption_papers(Header &h, package &guard, struct sockaddr_in &ip_s_g)
    {
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=guard.id_client;
        this->message.sequence_number=guard.sequence_number;
        this->message.acknowledge_number=guard.acknowledge_number;
        this->message.id_server_guardian=guard.header.id_server_source;
        this->message.ip_server_guardian=ip_s_g;
        this->message.header=h.message.header;
        this->message.header.type_message=ADOPTION_PAPERS;
        this->message.header.offset=ADOPTION_PAPERS_OFFSET;
        this->message.header.flag=NO_FLAG;      
    }
    
    Adoption_papers (package &adpt_p)
    {   
        bzero((char *) &this->message, sizeof(this->message));
        Get_time_message(this->message.date_time);
        this->message.id_client=adpt_p.id_client;
        this->message.sequence_number=adpt_p.sequence_number;
        this->message.acknowledge_number=adpt_p.acknowledge_number;
        this->message.id_server_guardian=adpt_p.id_server_guardian;
        this->message.ip_server_guardian=adpt_p.ip_server_guardian;
        this->message.header=adpt_p.header;
        this->message.header.type_message=ADOPTION_CONFIRMATION;
        this->message.header.offset=ADOPTION_PAPERS_OFFSET;
        this->message.header.flag=NO_FLAG;  
    }  
    virtual ~Adoption_papers(){}

private:
    static const unsigned short ADOPTION_PAPERS_OFFSET=sizeof(size.adoption); 
};

void print_output(package &message){
    
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Tipo de mensaje:          | "<<int(message.header.type_message) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Version:                  | "<< int(message.header.version) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Dominio:                  | "<<int(message.header.domain) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Id_s_dst:                 | " <<int(message.header.id_server_destination) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Id_s_src:                 | " <<int(message.header.id_server_source) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Flags:                    | "<<int(message.header.flag) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Offset:                   | " <<int(message.header.offset) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Número de sequencia:      | "<<message.sequence_number<< std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Siguiente # de sequencia: | "<<message.next_sequence_number<< std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Acknowledge:              | "<<message.acknowledge_number<< std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Id del servidor guardian: | "<<message.id_server_guardian<< std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Ip del servidor guardian: | "<<inet_ntoa(message.ip_server_guardian.sin_addr) << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Bookmark-nombre:          | "<< message.bookmark.nombre << std::endl;
    std::cout <<"-----------------------------------------------------"<<std::endl;
    std::cout <<"Bookmark-tiempo:          | "<< message.bookmark.tiempo << std::endl;
    std::cout <<"-----------------------------------------------------\n\n"<<std::endl;

}*/